package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.*;
import com.lagou.edu.configuration.SpringConfiguration;
import com.lagou.edu.utils.ClassScanUtil;
import com.lagou.edu.utils.TransactionManager;
import sun.nio.cs.ext.IBM037;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 注解加载类
 */
public  class AnnotationApplicationContent {

    private static final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); //一级缓存


    {
        try {
            //查询配置文件
            Configuration configuration = SpringConfiguration.class.getAnnotation(Configuration.class);
            String[] packages =  configuration.packages();
            Set<Class<?>>  classes =   scanPackageForClass(packages);
            //需要注入的bean
            Set<Class<?>>  springClasses =   filterSpringBean(classes);
            for (Class<?> clazz :springClasses ) {
                doCreateBean(clazz);
            }

            for (Map.Entry<String,Object> entry : singletonObjects.entrySet()) {
                //属性注入
                injectBean(entry.getValue());
                if (entry.getValue().getClass().getAnnotation(Transactional.class) !=null){
                    //事务包装
                    singletonObjects.put(entry.getKey(),transProxy(entry.getValue()));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private  void injectBean(Object bean) throws Exception {
        List<Field> fieldList = findInjectBean(bean.getClass());
        for (Field field : fieldList) {
            Object  injectBean =  getOneBean(field.getType());
            field.setAccessible(true);
            field.set(bean,injectBean);
        }

    }


    /**
     * 根据name获取bean
     * @param name
     * @return
     */
    public static Object getBean(String name){
       return singletonObjects.get(name);
    }


    /**
     * 根据类型获取bean
     * @param clazz
     * @return
     */
    public Map<String,Object> getBean(Class<?> clazz){
        Map<String,Object> beanMap = new HashMap<String,Object>();
        Enumeration<String> keys =  singletonObjects.keys();
        while (keys.hasMoreElements()){
            String key  =   keys.nextElement();
            Object object = singletonObjects.get(key);
            if (clazz.isInstance(object)){
                beanMap.put(key, object);
            }
         }
        return  beanMap;
    }


    /**
     * 根据类型获取bean
     * @param clazz
     * @return
     */
    public Object getOneBean(Class<?> clazz) throws Exception {
        Map<String,Object> beanMap = getBean(clazz);
        if (beanMap.size() == 1){
           return beanMap.values().stream().findFirst().get();
        }else {
            throw new Exception("获取bean失败");
        }
    }


    /**
     * 根据name和类型获取bean
     * @param name
     * @param clazz
     * @return
     */
    public Object getBean(String name , Class clazz){
        Map<String,Object> beanMap =  getBean(clazz);
        return  beanMap.get(name);
    }




    private static void doCreateBean(Class beanClass) throws IllegalAccessException, InstantiationException {
        String beanId = getBeanId(beanClass);
        Object bean =  beanClass.newInstance();
        singletonObjects.put(beanId,bean);
    }




    private static Object transProxy(Object object){
        TransactionProxy transactionProxy = new TransactionProxy();
        TransactionManager transactionManager = (TransactionManager) singletonObjects.get("transactionManager");
        if (object.getClass().getInterfaces().length > 1){
          return   transactionProxy.getJdkProxy(object,transactionManager);
        }else {
          return    transactionProxy.getCglibProxy(object,transactionManager);
        }
    }



    /*private static Object doCreateBean(Class beanClass) throws IllegalAccessException, InstantiationException {
        String beanId = getBeanId(beanClass);
        Object bean = null;
        //一级缓存中获取bean
        Object singletonBean =  singletonObjects.get(beanId);
        if (singletonBean == null){
            Object earlyBean =  earlySingletonObjects.get(beanId);
            if (earlyBean == null){
                Object  singleBean =  singletonFactories.get(beanId);
                if (singleBean == null){
                    //实例化bean
                    Object newBean = beanClass.newInstance();
                    singletonFactories.put(beanId,newBean);
                    bean = newBean;
                }else {
                    bean = singleBean;
                }
            }else{
                bean =  earlyBean;
            }
        }else {
            bean = singletonBean;
        }

        //查询需要注入的内容
        List<Field> fieldList = findInjectBean(beanClass);
        for (Field field : fieldList) {
            doCreateBean(field.getType());
        }
        return bean;
    }*/


    private static List<Field> findInjectBean(Class<?> clazz){
        List<Field> fieldList = new ArrayList<Field>();
        for (Field field:clazz.getDeclaredFields()) {
            if (field.getAnnotation(Autowired.class) != null){
                fieldList.add(field);
            }
        }
        return fieldList;
    }




    /**
     * 扫描出所有需要进行处理的类（bean）
     * @return
     */
    private static Set<Class<?>>  scanPackageForClass(String[] packages ){
        Set<Class<?>> classes = new HashSet<Class<?>>();
        for (String packagePath :packages ) {
            classes.addAll(ClassScanUtil.getClasses(packagePath));
        }
        return classes;
    }


    /**
     * 过滤出来需要处理的SpringBean对象类
     * @return
     */
    private static Set<Class<?>> filterSpringBean(Set<Class<?>> classes){

        Set<Class<?>> springClass = new HashSet<Class<?>>();
        for (Class<?> aClass : classes) {
            if (aClass.getAnnotation(Service.class) != null || aClass.getAnnotation(Component.class) != null  ||aClass.getAnnotation(Repository.class) != null ){
                springClass.add(aClass);
            }
        }
        return springClass;
    }




    private static String getBeanId(Class<?> beanClass){
        String id = "";
        if (beanClass.getAnnotation(Service.class) != null ){
            id = beanClass.getAnnotation(Service.class).value();
        }else if (beanClass.getAnnotation(Repository.class) != null ){
            id = beanClass.getAnnotation(Repository.class).value();
        }else if (beanClass.getAnnotation(Component.class) != null ){
            id = beanClass.getAnnotation(Component.class).value();
        }
        return StringUtils.isEmpty(id)?lowerFirstCase(beanClass.getSimpleName()):id ;
    }


    /**
     * 首字母小写
     * @param str
     * @return
     */
    public static String lowerFirstCase(String str){
        char[] chars = str.toCharArray();
        //首字母小写方法，大写会变成小写，如果小写首字母会消失
        chars[0] +=32;
        return String.valueOf(chars);
    }





}
